import {isArray, isPlainObject} from 'lodash';
import qs from 'qs';
import {
  AUTHORIZATION,
  CONTENT_TYPE_FORM,
  CONTENT_TYPE_JSON,
  CONTENT_TYPE_OCTET_STREAM,
  DEFAULT_CHARSET,
  DEFAULT_CONTENT_TYPE,
  ERROR_TYPE,
  HTTP_MESSAGE,
} from './const'

let getToken = () => {
  throw new Error('请调用configRequest配置getToken函数')
};
let fetchFn =
  window.fetch ||
  (async () => {
    throw new Error('请调用configRequest配置fetch函数')
  });

// noinspection JSUnusedGlobalSymbols
export const configRequest = (config = {}) => {
  if (config.getToken) {
    ({
      getToken
    } = config)
  }
  if (config.fetch) {
    fetchFn = config.fetch
  }
};

function checkStatus(response) {
  const state = response.status;
  if (
    (state >= 200 && state < 300) ||
    state === 400 || // 400用户服务端数据验证错误，在通用状态错误检查中排除
    state === 401 // 401用户token验证错误，在通用状态错误检查中排除
  ) {
    return response
  }
  const errortext = HTTP_MESSAGE[state] || response.statusText;
  const error = new Error(errortext);
  error.type = ERROR_TYPE.HTTP;
  error.name = state;
  error.response = response;
  throw error
}

function checkResponseData(respData) {
  if (typeof respData === 'string') {
    //跳转错误页面
    if (respData.indexOf('!DOCTYPE') >= 0) {
      localStorage.removeItem('errorBackHtml');
      localStorage.setItem('errorBackHtml', respData);
      let url = window.location.href;
      window.location.href = url.substr(0, url.indexOf("newHrm")) + 'errorBack.html';
    } else {
      respData = JSON.parse(respData);
    }
  }
  // noinspection JSUnresolvedVariable
  if (respData.retCode === 0) {
    return respData.data
  } else { // noinspection JSUnresolvedVariable
    if (!respData.retCode) {
        return respData
      } else {
        // noinspection JSUnresolvedVariable
      const error = new Error(respData.retMsg);
        error.type = ERROR_TYPE.BUSINESS;
        // noinspection JSUnresolvedVariable
      error.name = respData.retMsg;
        error.code = respData.retCode;
        throw error
      }
  }
}

function normalizedUrl(url) {
  return url.replace(/[?&]+$/, '')
}

function parseParamsToUrl(url, params) {
  let p = params || '';
  if (isPlainObject(params)) {
    p = qs.stringify(params, {
      arrayFormat: 'repeat'
    })
  }
  return normalizedUrl(`${url}${url.includes('?') ? '&' : '?'}${p}`)
}

function normalizeFormData(url, options) {
  const {
    params,
    ...rest
  } = options;
  return {
    url: parseParamsToUrl(url, params),
    options: rest,
  }
}

function normalizeJsonData(url, options) {
  const {
    params,
    body,
    headers = {},
    ...rest
  } = options;
  const contentType = `${CONTENT_TYPE_JSON}; charset=${DEFAULT_CHARSET}`;
  const bodyStr = JSON.stringify(body);
  const fukkHeaders = {
    'Content-Type': contentType,
    ...headers,
  };
  return {
    url: parseParamsToUrl(url, params),
    options: {
      ...rest,
      body: bodyStr,
      headers: fukkHeaders,
    },
  }
}

function normalizeParamsData(url, options) {
  const {
    params,
    headers = {},
    ...rest
  } = options;
  const contentType = `${CONTENT_TYPE_FORM}; charset=${DEFAULT_CHARSET}`;
  const body = qs.stringify(params, {
    arrayFormat: 'repeat'
  });
  const fukkHeaders = {
    'Content-Type': contentType,
    ...headers,
  };
  return {
    url: normalizedUrl(url),
    options: {
      ...rest,
      body,
      headers: fukkHeaders,
    },
  }
}

function normalizeCommonData(url, options) {
  return {
    url: normalizedUrl(url),
    options,
  }
}

function normalizeSimpleData(url, options) {
  const {
    params = {}, body = {}, ...rest
  } = options;
  return {
    url: parseParamsToUrl(url, {
      ...params,
      ...body
    }),
    options: rest,
  }
}

const defaultOptions = {
  credentials: 'include',
  // mode: 'cors',
  cache: 'default',
  headers: {
    Accept: DEFAULT_CONTENT_TYPE,
    // 'Access-Control-Allow-Origin': '*',
  },
};

function getNormaLizeRequest(url, options) {
  // eslint-disable-next-line
  let normalized;
  const {
    method = 'GET', ...restOptions
  } = options;
  const {
    body,
    params
  } = restOptions;
  const isFormData = body ? body instanceof FormData : false;
  if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
    if (isFormData) {
      normalized = normalizeFormData(url, options)
    } else if (isPlainObject(body) || isArray(body)) {
      normalized = normalizeJsonData(url, options)
    } else if (!body && isPlainObject(params)) {
      normalized = normalizeParamsData(url, options)
    } else {
      normalized = normalizeCommonData(url, options)
    }
  } else {
    normalized = normalizeSimpleData(url, options)
  }
  const {
    url: normalizeUrl,
    options: normalizeOptions
  } = normalized;
  //const token = getToken()
  const {
    headers: defaultHeaders,
    ...restDefaultOptions
  } = defaultOptions;
  //const fakeMethod = method === 'GET' ? 'GET' : 'POST'
  const fakeMethod = method; //{ GET: 'GET', POST: 'POST', PUT: 'PUT', DELETE: 'DELETE', PATCH:'PATCH' };
  normalizeOptions.headers = {
    ...defaultOptions.headers,
    ...normalizeOptions.headers,
    method: method.toUpperCase(),
  };
  // if (token) {
  //   normalizeOptions.headers[AUTHORIZATION] = token
  // }
  return {
    normalizeUrl,
    normalizeOptions: {
      ...restDefaultOptions,
      ...normalizeOptions,
      method: fakeMethod,
    },
  }
}
// export const delay = timeout => (callback) => {
//   return new Promise((resolve) => {
//     setTimeout(() => {
//       resolve(callback())
//     }, timeout)
//   })
// }

function getResponseData(response) {
  if (response.status === 204) {
    return response.text()
  }
  const contentType = response.headers.get('Content-Type');
  if (contentType.indexOf("text/json") >= 0 || contentType.indexOf("application/json") >= 0) {
    return response.text()
  } else if (contentType.indexOf("text/html") >= 0) {
    return response.text()
  } else if (contentType.indexOf(CONTENT_TYPE_OCTET_STREAM) >= 0) {
    return response.blob()
  } else {
    return response.text()
  }
}
/**
 * 请求一个地址，并返回promise
 * 注意: 请求错误及后台错误已委托框架处理，若需自己处理，请自行catch
 * @param  {string} url 请求地址
 * @param  {object} [options] {
 *   method --string GET|POST|PUT|DELETE
 *   headers --object个地址，并返回promise
 *   isMock --boolean 是否使用mock接口
 *   isService --boolean 是否调用服务化接口，默认为否
 *   dsfKey --string 服务化接口标识，默认为空
 * 注意: 请求错误及后台错误已委托框架处理，若需自己处理，请自行catch
 * @param  {string} url 请求地址
 *   params --object 请求参数，若method为get或和body共存时，将被放入queryString
 *   body --object 复杂对象传递，专用于application/json，若method为get，则放入queryString
 * }
 * @return {object} 若成功，则返回数据对象，否则抛出错误
 */
export default async function request(url, options = {}) {
  //mock调用flowc统一封装
  // noinspection JSUnresolvedVariable
  url = fixFlowActionNameEffect(url, options.params && options.params.flowActionName, options.isMock);
  //服务化接口调用统一封装
  // noinspection JSUnresolvedVariable
  if (options.isService) {
    let dsfService = dsfUnifiedCall(url, options.params, options.dsfKey = false);
    url = dsfService.url;
    options.params = dsfService.params
  }

  options.params = {
    ...options.params,
    requestTimeStamp: new Date().getTime()
  };
  const {
    normalizeUrl,
    normalizeOptions
  } = getNormaLizeRequest(url, options);
  return fetchFn(normalizeUrl, normalizeOptions)
    .then(checkStatus)
    .then(getResponseData)
    .then(checkResponseData)
}

/**
 * 用于解决接口使用flowActionName参数 区分接口造成yapi接口无法建立的问题
 * @param {string} flowc 
 * @param {string} flowActionName 
 * @param {boolean} isMock 是否使用mock接口, 默认不使用
 */
function fixFlowActionNameEffect(flowc, flowActionName, isMock = false) {
  //开发环境中默认不使用mock，可切换，生成环境不使用mock
  // noinspection ES6ModulesDependencies
  return isMock && process.env.NODE_ENV === 'development' ? `${flowc}/${flowActionName}` : flowc;
}

/**
 * 对服务化接口调用进行统一封装
 * @param {string} url 服务化接口路径
 * @param {Object} params 服务化接口入参
 * @param {boolean} key 服务化接口调用标识，默认为空
 */
function dsfUnifiedCall(url, params, key = false) {
  const { dse_sessionId, ...info } = params;
  // noinspection UnnecessaryLocalVariableJS
  let dsfService = {
    url: '/icbc/hrcms/uni_interface.flowc',
    params: {
      flowActionName: 'dsfUniCall',
      url: url,
      info: JSON.stringify(info),
      key: key,
      dse_sessionId: dse_sessionId
    }
  };
  return dsfService
}

// noinspection JSUnusedGlobalSymbols
/**
 * 生成请求图片地址
 * @param {string} busiId eap批次号
 * @param {boolean} noToken 无需token
 */
export const requestImageUrl = (busiId, noToken) => {
  if (noToken) {
    return busiId ? `/thirdpart/file/image/${busiId}` : ''
  } else {
    const token = getToken();
    return busiId ? `/file/image/${busiId}?${qs.stringify({ [AUTHORIZATION]: token })}` : ''
  }
};

// noinspection JSUnusedGlobalSymbols
/**
 * 生成请求附件地址
 * @param {string} busiId eap批次号
 */
export const requestAttachmentUrl = busiId => {
  const token = getToken();
  return busiId ? `/file/attachment/${busiId}?${qs.stringify({ [AUTHORIZATION]: token })}` : ''
};

// noinspection JSUnusedGlobalSymbols
/**
 * 生成二维码地址
 * @param {string} seqId 二维码id
 * @param {int} width 二维码尺寸
 */
export const requestQrcodeImageUrl = (seqId, width = 100) => {
  const token = getToken();
  return seqId ?
    `/qrcode/getqrcodewithseqid?${qs.stringify({ [AUTHORIZATION]: token, seqId, width })}` :
    ''
};
// noinspection JSUnusedGlobalSymbols
/**
 * 生成二维码附件地址
 * @param {string} seqId 二维码id
 * @param {int} width 二维码尺寸
 */
export const requestQrcodeAttachmentUrl = (seqId, width = 100) => {
  const token = getToken();
  return seqId ?
    `/qrcode/getqrcodewithseqid?${qs.stringify({
      [AUTHORIZATION]: token,
      seqId,
      width,
      type: 'attachment',
    })}` :
    ''
};

// noinspection JSUnusedGlobalSymbols
export const requestOneFileUploadProps = () => ({
  action: '/file/uploadOneFile',
  name: 'oneFile',
  headers: {
    [AUTHORIZATION]: getToken(),
  },
});
